package cn.jtfadmin.base.shiro.support;

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.spring.config.AbstractShiroConfiguration;
import org.apache.shiro.util.StringUtils;

import javax.cache.configuration.MutableConfiguration;
import javax.cache.expiry.Duration;
import javax.cache.expiry.TouchedExpiryPolicy;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * shiro cache manager, 通过jCacheManager 代理
 *
 * @author jtf
 * @see AbstractShiroConfiguration#cacheManager
 * @see AbstractShiroConfiguration#securityManager(List)
 * @since 0.0.1
 */

public class ShiroCacheManager implements CacheManager {

    private javax.cache.CacheManager jCacheManager;

    public ShiroCacheManager(javax.cache.CacheManager jCacheManager) {
        this.jCacheManager = jCacheManager;
    }

    public javax.cache.CacheManager getjCacheManager() {
        return jCacheManager;
    }

    public void setjCacheManager(javax.cache.CacheManager jCacheManager) {
        this.jCacheManager = jCacheManager;
    }

    @Override
    public <K, V> Cache<K, V> getCache(String name) throws CacheException {
        if (!StringUtils.hasText(name)) {
            throw new IllegalArgumentException("Cache name cannot be null or empty.");
        } else {
            Iterable<String> cacheNames = jCacheManager.getCacheNames();
            boolean existCache = false;
            Iterator<String> iterator = cacheNames.iterator();
            while (iterator.hasNext()) {
                String cacheName = iterator.next();
                if (Objects.equals(cacheName, name)) {
                    existCache = true;
                    break;
                }

            }
            if (existCache) {
                return new ShiroCache(jCacheManager.getCache(name));
            } else {
                try {
                    return new ShiroCache(createCache(name));
                } catch (Exception ex) {
                    return new ShiroCache(jCacheManager.getCache(name));
                }

            }

        }
    }


    protected <K, V> javax.cache.Cache<K, V> createCache(String name) {
        MutableConfiguration<K, V> mutableConfiguration = new MutableConfiguration<>();
        // 一天时间过期
        mutableConfiguration.setExpiryPolicyFactory(TouchedExpiryPolicy.factoryOf(new Duration(TimeUnit.DAYS, 30L)));
        try {
            return jCacheManager.createCache(name, mutableConfiguration);
        } catch (Exception e) {
            throw new CacheException("缓存创建异常");
        }
    }

    class ShiroCache<K, V> implements Cache<K, V> {


        private javax.cache.Cache<K, V> jCache;

        public ShiroCache(javax.cache.Cache<K, V> jCache) {
            this.jCache = jCache;
        }

        @Override
        public V get(K k) throws CacheException {
            return jCache.get(k);
        }

        @Override
        public V put(K k, V v) throws CacheException {
            jCache.put(k, v);
            return v;
        }

        @Override
        public V remove(K k) throws CacheException {
            V v = jCache.get(k);
            jCache.remove(k);
            return v;
        }

        @Override
        public void clear() throws CacheException {
            jCache.removeAll();
        }

        @Override
        public int size() {
            Iterator<javax.cache.Cache.Entry<K, V>> iterator =
                    jCache.iterator();
            int i = 0;
            while (iterator.hasNext()) {
                iterator.next();
                i++;
            }
            return i;
        }

        @Override
        public Set<K> keys() {
            Iterator<javax.cache.Cache.Entry<K, V>> iterator =
                    jCache.iterator();
            Set<K> set = new HashSet<>();
            while (iterator.hasNext()) {
                set.add(iterator.next().getKey());
            }
            return set;
        }

        @Override
        public Collection<V> values() {
            Iterator<javax.cache.Cache.Entry<K, V>> iterator =
                    jCache.iterator();
            List<V> list = new LinkedList<>();
            while (iterator.hasNext()) {
                list.add(iterator.next().getValue());
            }
            return list;
        }
    }

}
